#include <stdio.h>
#include "../include/sqliteInt.h"
#include "../include/commonutils.h"

static void execute_sql(sqlite3 *ppDb, const char *zSql);

/*
** Run a prepared statement
*/
void exec_prepared_stmt(int cMode, sqlite3_stmt *pStmt);

int *my_callback(void *, int, char **, char **);

int main() {
    const char *filename = "F:\\CLionProjects\\inspect-sqlite3\\db\\zhou.db";
    sqlite3 *ppDb;
    sqlite3_open(filename, &ppDb);
    execute_sql(ppDb, "select date('now') as date, 1, 2, 3, 4, 5;");
    sqlite3_close(ppDb);
//    char **errmsg = NULL;
//    sqlite3_exec(ppDb, "select date('now') as date, 1, 2, 3, 4, 5;",
//                 (int (*)(void *, int, char **, char **)) my_callback, NULL, errmsg);
    return 0;
}

static void execute_sql(sqlite3 *ppDb, const char *zSql) {
    sqlite3_stmt *pStmt = NULL;
    const char *zLeftover;

    int rc = sqlite3_prepare_v2(ppDb, zSql, -1, &pStmt, &zLeftover);
    if (rc != SQLITE_OK) {
        printf("%s\n", "execute error.");
        return;
    }
    exec_prepared_stmt(MODE_Table, pStmt);
}

void exec_prepared_stmt(int cMode, sqlite3_stmt *pStmt) {
    int rc = sqlite3_step(pStmt);

    if (rc != SQLITE_ROW) {
        return;
    }

    int nColumn = sqlite3_column_count(pStmt);

    if (nColumn == 0) {
        return;
    }

    sqlite3_int64 nAlloc = nColumn * 4;

    if (nAlloc <= 0) {
        nAlloc = 1;
    }

    char **azData = sqlite3_malloc64(nAlloc * sizeof(char *));
    if (azData == 0) {
        out_of_memory();
    }

    for (int i = 0; i < nColumn; i++) {
        azData[i] = strdup(sqlite3_column_name(pStmt, i));
    }

    sqlite3_int64 nRow = 0;
    const char *z;
    do {
        if ((nRow + 2) * nColumn >= nAlloc) {
            nAlloc *= 2;
            azData = sqlite3_realloc64(azData, nAlloc * sizeof(char *));
            if (azData == 0) out_of_memory();
        }
        nRow++;
        for (int i = 0; i < nColumn; i++) {
            z = (const char *) sqlite3_column_text(pStmt, i);
            azData[nRow * nColumn + i] = z ? strdup(z) : 0;
        }
    } while ((rc = sqlite3_step(pStmt)) == SQLITE_ROW);

    int nWidth = 0;
    int *colWidth = NULL;
    int *actualWidth = NULL;

    if (nColumn > nWidth) {
        colWidth = realloc(colWidth, nColumn * 2 * sizeof(int));
        if (colWidth == 0) {
            out_of_memory();
        }
        for (int i = nWidth; i < nColumn; i++) {
            colWidth[i] = 0;
        }
        nWidth = nColumn;
        actualWidth = &colWidth[nColumn];
    }

    memset(actualWidth, 0, nColumn * sizeof(int));
    int w;
    for (int i = 0; i < nColumn; i++) {
        w = colWidth[i];
        if (w < 0) {
            w = -w;
        }
        actualWidth[i] = w;
    }

    // The text to print when a NULL comes back from the database
    char nullValue[20];

    int nTotal = nColumn * (nRow + 1);
    for (int i = 0, j, n; i < nTotal; i++) {
        z = azData[i];
        if (z == 0) {
            z = nullValue;
        }
        n = strlenChar(z);
        j = i % nColumn;
        if (n > actualWidth[j]) {
            actualWidth[j] = n;
        }
    }

    const char *colSep = 0;
    const char *rowSep = 0;

    if (cMode == MODE_Table) {
        colSep = " | ";
        rowSep = " |\n";
        print_row_separator(stdout, actualWidth, nColumn, "+");
        fputs("| ", stdout);
        for (int i = 0, n; i < nColumn; i++) {
            w = actualWidth[i];
            n = strlenChar(azData[i]);
            fprintf(stdout, "%*s%s%*s", (w - n) / 2, "", azData[i], (w - n + 1) / 2, "");
            fputs(i == nColumn - 1 ? " |\n" : " | ", stdout);
        }
        print_row_separator(stdout, actualWidth, nColumn, "+");
    }

    for (int i = nColumn, j = 0; i < nTotal; i++, j++) {
        if (j == 0 && cMode != MODE_Column) {
            fprintf(stdout, "%s", "| ");
        }
        z = azData[i];
        if (z == 0) {
            z = nullValue;
        }
        w = actualWidth[j];
        if (colWidth[j] < 0) {
            w = -w;
        }
        utf8_width_print(stdout, w, z);
        if (j == nColumn - 1) {
            fprintf(stdout, "%s", rowSep);
            j = -1;
        } else {
            fprintf(stdout, "%s", colSep);
        }
    }
    if (cMode == MODE_Table) {
        print_row_separator(stdout, actualWidth, nColumn, "+");
    }
    sqlite3_int64 nData = (nRow + 1) * nColumn;
    for (int i = 0; i < nData; i++) {
        free(azData[i]);
    }
    sqlite3_free(azData);
}

/**
 * 执行回调函数
 * @param pArg
 * @param nArg      Number of result columns
 * @param azArg     Text of each result column
 * @param azCol     Column names
 * @return
 */
int *my_callback(void *pArg, int nArg, char **azArg, char **azCol) {
    int w = 5;
    const char *nullValue = "";
    for (int i = 0; i < nArg; i++) {
        int len = strlen30(azCol[i] ? azCol[i] : "");
        if (len > w) w = len;
    }
    char *rowSeparator = "\n";
    for (int i = 0; i < nArg; i++) {
        fprintf(stdout, "%*s = %s%s", w, azCol[i], azArg[i] ? azArg[i] : nullValue, rowSeparator);
    }
    return NULL;
}